﻿using System;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace VsTortoiseSVN.Controls
{
    /// <summary>
    /// ListView2 is a ListView that provides a default item sorter
    /// and sort icons in column headers.
    /// </summary>
    public class ListView2 : ListView
    {
        private ListViewStringItemSorter _defaultSorter = new ListViewStringItemSorter();

        public ListView2()
            : base()
        {
            ListViewItemSorter = _defaultSorter;
            View = View.Details;
            Sorting = SortOrder.Ascending;
        }

        protected override void OnColumnClick(ColumnClickEventArgs e)
        {
            base.OnColumnClick(e);

            SortOrder sorting = OnGetColumnSorting(e.Column);
            OnSetColumnSorting(e.Column, sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending);

            for (int column = 0; column < Columns.Count; ++column)
            {
                SortOrder columnSorting = OnGetColumnSorting(column);
                ShowHeaderIcon(column, columnSorting);
            }

            Sort();
        }

        protected virtual SortOrder OnGetColumnSorting(int columnIndex)
        {
            if (ListViewItemSorter == _defaultSorter && _defaultSorter.Column == columnIndex)
                return _defaultSorter.Sorting;

            return SortOrder.None;
        }

        protected virtual void OnSetColumnSorting(int columnIndex, SortOrder sorting)
        {
            _defaultSorter.Column = columnIndex;
            _defaultSorter.Sorting = sorting;
        }

        private void ShowHeaderIcon(int columnIndex, SortOrder sortOrder)
        {
            if (columnIndex < 0 || columnIndex >= Columns.Count)
                return;

            IntPtr hHeader = SendMessage(Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
            ColumnHeader colHdr = Columns[columnIndex];

            HDITEM hd = new HDITEM();
            hd.mask = HDI_FORMAT;

            HorizontalAlignment align = colHdr.TextAlign;
            if (align == HorizontalAlignment.Left)
                hd.fmt = HDF_LEFT | HDF_STRING | HDF_BITMAP_ON_RIGHT;
            else if (align == HorizontalAlignment.Center)
                hd.fmt = HDF_CENTER | HDF_STRING | HDF_BITMAP_ON_RIGHT;
            else        // HorizontalAlignment.Right
                hd.fmt = HDF_RIGHT | HDF_STRING;

            if (sortOrder == SortOrder.Ascending)
                hd.fmt |= HDF_SORTUP;
            else if (sortOrder == SortOrder.Descending)
                hd.fmt |= HDF_SORTDOWN;

            SendMessage2(hHeader, HDM_SETITEM, new IntPtr(columnIndex), ref hd);
        }

        #region Win32
        [StructLayout(LayoutKind.Sequential)]
        private struct HDITEM
        {
            public Int32 mask;
            public Int32 cxy;
            [MarshalAs(UnmanagedType.LPTStr)]
            public String pszText;
            public IntPtr hbm;
            public Int32 cchTextMax;
            public Int32 fmt;
            public Int32 lParam;
            public Int32 iImage;
            public Int32 iOrder;
        };

        [DllImport("user32")]
        static extern IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32", EntryPoint = "SendMessage")]
        static extern IntPtr SendMessage2(IntPtr Handle, Int32 msg, IntPtr wParam, ref HDITEM lParam);

        const Int32 HDI_WIDTH = 0x0001;
        const Int32 HDI_HEIGHT = HDI_WIDTH;
        const Int32 HDI_TEXT = 0x0002;
        const Int32 HDI_FORMAT = 0x0004;
        const Int32 HDI_LPARAM = 0x0008;
        const Int32 HDI_BITMAP = 0x0010;
        const Int32 HDI_IMAGE = 0x0020;
        const Int32 HDI_DI_SETITEM = 0x0040;
        const Int32 HDI_ORDER = 0x0080;
        const Int32 HDI_FILTER = 0x0100;                // 0x0500

        const Int32 HDF_LEFT = 0x0000;
        const Int32 HDF_RIGHT = 0x0001;
        const Int32 HDF_CENTER = 0x0002;
        const Int32 HDF_JUSTIFYMASK = 0x0003;
        const Int32 HDF_RTLREADING = 0x0004;
        const Int32 HDF_OWNERDRAW = 0x8000;
        const Int32 HDF_STRING = 0x4000;
        const Int32 HDF_BITMAP = 0x2000;
        const Int32 HDF_BITMAP_ON_RIGHT = 0x1000;
        const Int32 HDF_IMAGE = 0x0800;
        const Int32 HDF_SORTUP = 0x0400;                // 0x0501
        const Int32 HDF_SORTDOWN = 0x0200;              // 0x0501

        const Int32 LVM_FIRST = 0x1000;         // List messages
        const Int32 LVM_GETHEADER = LVM_FIRST + 31;

        const Int32 HDM_FIRST = 0x1200;         // Header messages
        const Int32 HDM_SETIMAGELIST = HDM_FIRST + 8;
        const Int32 HDM_GETIMAGELIST = HDM_FIRST + 9;
        const Int32 HDM_GETITEM = HDM_FIRST + 11;
        const Int32 HDM_SETITEM = HDM_FIRST + 12;
        #endregion
    }

    internal class ListViewStringItemSorter : IComparer
    {
        public int Column
        {
            get;
            set;
        }

        public SortOrder Sorting
        {
            get;
            set;
        }

        public ListViewStringItemSorter()
        {
            Column = 0;
            Sorting = SortOrder.Ascending;
        }

        public int Compare(object a, object b)
        {
            ListViewItem itemA = a as ListViewItem;
            ListViewItem itemB = b as ListViewItem;

            int result = 0;
            if (Column == 0)
                result = string.Compare(itemA.Text, itemB.Text);
            else
                result = string.Compare(itemA.SubItems[Column].Text, itemB.SubItems[Column].Text);

            return Sorting == SortOrder.Ascending ? result : -result;
        }
    }
}
